using System;
using System.Collections.Generic;
using System.Linq;

namespace Mbs
{
    [Serializable]
    public class MapService : IMapService
    {
        private Dictionary<RuntimeTypeHandle, RuntimeTypeHandle> Map = new Dictionary<RuntimeTypeHandle, RuntimeTypeHandle>();

        public IMapService Register<TSource, TTarget>()
        {
            var sourceType = typeof(TSource).TypeHandle;
            var type = typeof(TTarget).TypeHandle;


            if (Map.ContainsKey(sourceType))
                Map.Remove(sourceType);
            Map[sourceType] = type;

            return this;
        }

        public IMapService UnRegister<TSource>()
        {
            var sourceType = typeof(TSource).TypeHandle;

            if (Map.ContainsKey(sourceType))
                Map.Remove(sourceType);

            return this;
        }


        public TTarget Create<TSource, TTarget>()
        {
            return (TTarget)Create(typeof(TSource));
        }

        public TTarget Create<TTarget>(Type sourceType)
        {
            return (TTarget)Create(sourceType);
        }

        public object Create(Type sourceType)
        {
            if (sourceType == null)
                throw new ArgumentNullException("sourceType");

            if (Map.ContainsKey(sourceType.TypeHandle))
                return Activator.CreateInstance(Type.GetTypeFromHandle(Map[sourceType.TypeHandle]));

            return FindAssignableElement(sourceType);

        }

        private object FindAssignableElement(Type sourceType)
        {
            var tmp = Map.Reverse();
            object result = null;

            foreach (KeyValuePair<RuntimeTypeHandle, RuntimeTypeHandle> pair in tmp)
            {
                var key = Type.GetTypeFromHandle(pair.Key);
                if (key.IsAssignableFrom(sourceType))
                {
                    var value = Type.GetTypeFromHandle(pair.Value);
                    result = Activator.CreateInstance(value);
                    break;
                }
            }
            return result;
        }

        public void Dispose()
        {
            Map.Clear();
        }
    }
}
